package progressbar

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/leminewx/gokit/util/pool"
)

const (
	_DEFAULT_TERMINAL_SIZE    = 80 // 默认终端长度
	_DEFAULT_BAR_RIGHT_MARGIN = 50 // 默认进度条右侧内边距

	_BAR_MIN_SIZE = 10  // 进度条最小长度
	_BAR_MAX_SIZE = 100 // 进度条最大长度
)

var spinners = []rune{'⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽', '⣾'}

type Style [2]rune

var (
	STYLE_RHOMBUS Style = [2]rune{'▰', '▱'} // 菱形

	STYLE_BAR_POINT Style = [2]rune{'█', '·'} // 条形/点
	STYLE_BAR_EMPTY Style = [2]rune{'█', ' '} // 条形/空

	STYLE_SQUARE_POINT Style = [2]rune{'■', '·'} // 方形/点
	STYLE_SQUARE_EMPTY Style = [2]rune{'■', ' '} // 方形/空
)

type Type int8

const (
	TYPE_BYTES  = iota // 数据类型
	TYPE_NUMBER        // 数量类型
)

// Progressbar 定义进度条的数据结构
type Progressbar struct {
	// total 进度总量
	total int64
	// current 当前进度量
	current int64
	// isFinished 是否完成
	isFinished bool

	// hideCursor 隐藏光标
	hideCursor bool
	// autoResize 自动调整进度条的宽度
	autoResize bool
	// termWidth 终端宽度
	termWidth int
	// barMaxWidth 进度条的最大宽度
	barMaxWidth int
	// processMaxWidth 进度的最大宽度
	processMaxWidth int
	// speedMaxWidth 速度的最大宽度
	speedMaxWidth int

	// startTime 进度开始时间
	startTime time.Time
	// lastRender 最近渲染时间
	lastRender time.Time
	// renderTimeout 单次渲染超时时间
	renderTimeout time.Duration

	// barType 进度条类型
	barType Type
	// barStyle 进度条样式
	barStyle Style
	// barPrefix 进度条自定义前缀
	barPrefix string
	// spinnerIndex 上一个指示图标索引
	spinnerIndex int
	// spinners 进度加载的指示图标
	spinners []rune

	// speedCalculator 速度计算器
	speedCalculator *SpeedCalculator

	lock sync.RWMutex
}

// New 新建一个进度条
func New(total int64) *Progressbar {
	bar := &Progressbar{
		total:           total,
		startTime:       time.Now(),
		renderTimeout:   100 * time.Millisecond,
		autoResize:      true,
		hideCursor:      true,
		barStyle:        STYLE_SQUARE_POINT,
		spinners:        spinners,
		speedCalculator: NewSpeedCalculator(),
	}

	// 隐藏光标
	if bar.hideCursor {
		fmt.Print("\033[?25l")
	}

	// 初始化进度条最大宽度
	bar.initBarMaxWidth()
	return bar
}

// WithPrefix 设置进度条的前缀
//
// prefix ⣾ [■■■■■■■■■■·······]
func (own *Progressbar) WithPrefix(prefix string) *Progressbar {
	own.barPrefix = prefix
	return own
}

// WithSpinners 设置进度条的指示图标集
//
// 默认指示图标集：['⣾', '⣽', '⣻', '⢿', '⣷', '⣯', '⣟', '⡿']
func (own *Progressbar) WithSpinners(spinners []rune) *Progressbar {
	if len(spinners) > 0 {
		own.spinners = spinners
	}

	return own
}

// WithStyle 设置进度条的样式
//
// 默认：■
func (own *Progressbar) WithStyle(style Style) *Progressbar {
	own.barStyle = style
	return own
}

// WithType 设置进度条的类型
//
// 默认：TYPE_BYTES
func (own *Progressbar) WithType(typ Type) *Progressbar {
	own.barType = typ
	return own
}

// IsFinshed 检查进度是否完成
func (own *Progressbar) IsFinshed() bool {
	own.lock.RLock()
	isOver := own.isFinished
	own.lock.RUnlock()
	return isOver
}

// Add 增加新完成的进度
func (own *Progressbar) Add(increment int64) {
	own.lock.Lock()

	own.current += increment
	if own.current > own.total {
		own.current = own.total
	}
	own.render()

	own.lock.Unlock()
}

// Update 更新当前的总进度
func (own *Progressbar) Update(current int64) {
	own.lock.Lock()

	if current > own.total {
		current = own.total
	}

	own.current = current
	own.render()

	own.lock.Unlock()
}

// render 渲染
func (own *Progressbar) render() {
	if own.isFinished {
		return
	}

	// 校验渲染时间
	now := time.Now()
	if now.Sub(own.lastRender) < own.renderTimeout && own.current < own.total {
		return
	}
	own.lastRender = now

	// 重置进度条宽度
	if own.autoResize {
		own.initBarMaxWidth()
	}

	// 计算进度百分比
	percent := float64(own.current) / float64(own.total)
	// 计算已用时间（精确到秒）
	elapsed := time.Since(own.startTime).Round(time.Second)
	// 计算预计总耗时（动态计算）
	estimated := time.Duration(0)
	if percent > 0 {
		estimated = time.Duration(float64(elapsed) / percent).Round(time.Second)
	}
	// 计算剩余时间（精确到秒）
	remaining := (estimated - elapsed).Round(time.Second)

	// 构建进度条
	builder := pool.DefaultStringBuilderPool.Alloc()
	builder.WriteString("\033[2K\r")
	builder.WriteString(own.barPrefix)
	builder.WriteByte(' ')
	builder.WriteRune(own.spinners[own.spinnerIndex%len(own.spinners)])

	builder.WriteString(" [")
	own.spinnerIndex = (own.spinnerIndex + 1) % len(own.spinners)
	filled := int(math.Round(float64(own.barMaxWidth) * percent))
	builder.WriteString(strings.Repeat(string(own.barStyle[0]), filled))
	blank := own.barMaxWidth - filled
	builder.WriteString(strings.Repeat(string(own.barStyle[1]), blank))
	builder.WriteString("] ")

	builder.WriteString(fmt.Sprintf("%6.2f%% ", percent*100))
	builder.WriteString(" | ")
	own.calcProcess(builder)
	builder.WriteString(" | ")
	own.calcSpeed(builder)
	builder.WriteString(" | ")
	builder.WriteString(remaining.String())

	output := builder.String()
	pool.DefaultStringBuilderPool.Dealloc(builder)

	// 截断超过终端宽度的内容
	if len([]rune(output)) > own.termWidth {
		output = output[:own.termWidth]
	}

	fmt.Print(output)

	if own.current >= own.total {
		own.finish()
	}
}

// initBarMaxWidth 初始化进度条最大宽度
func (own *Progressbar) initBarMaxWidth() {
	// 获取终端宽度
	own.termWidth = own.getTerminalSize()

	// 计算进度条最大宽度
	switch own.barType {
	case TYPE_BYTES:
		total := len(strconv.Itoa(int(own.total)))
		own.processMaxWidth = total*2 + 1 // 123456789/123456789
		own.speedMaxWidth = total + 2     // 123456789/s
	default:
		total := len(formatBytesSize(float64(own.total)))
		own.processMaxWidth = total + 10 // 1023.40MB/1023.22TB
		own.speedMaxWidth = 11           // 1023.23MB/s
	}
	own.barMaxWidth = own.termWidth - (len(own.barPrefix) + own.processMaxWidth + own.speedMaxWidth + 30)

	// 设置进度条宽度
	if own.barMaxWidth < _BAR_MIN_SIZE {
		own.barMaxWidth = _BAR_MIN_SIZE
	} else if own.barMaxWidth > _BAR_MAX_SIZE {
		own.barMaxWidth = _BAR_MAX_SIZE
	}
}

// calcProcess 计算进度
func (own *Progressbar) calcProcess(builder *strings.Builder) {
	switch own.barType {
	case TYPE_NUMBER:
		builder.WriteString(strconv.Itoa(int(own.current)))
		builder.WriteByte('/')
		builder.WriteString(strconv.Itoa(int(own.total)))
	default:
		builder.WriteString(formatBytesSize(float64(own.current)))
		builder.WriteByte('/')
		builder.WriteString(formatBytesSize(float64(own.total)))
	}
}

// calcSpeed 计算速度
func (own *Progressbar) calcSpeed(builder *strings.Builder) {
	switch own.barType {
	case TYPE_NUMBER:
		builder.WriteString(strconv.FormatFloat(own.speedCalculator.Update(int(own.current)), 'f', 2, 64))
		builder.WriteString("/s")
	default:
		builder.WriteString(formatBytesSize(own.speedCalculator.Update(int(own.current))))
		builder.WriteString("/s")
	}
}

// finish 进度条完成
func (own *Progressbar) finish() {
	own.isFinished = true
	if own.hideCursor {
		fmt.Print("\033[?25h") // 显示光标
	}

	fmt.Printf("\033[2K\r%s 🎉🎉🎉  \033[32mCompleted! Cost: %s\033[0m\n", own.barPrefix, time.Since(own.startTime).Round(time.Second))
}

func formatBytesSize(size float64) string {
	switch {
	case size < 1024:
		return fmt.Sprintf("%.2fB", float64(size))
	case size < 1024*1024:
		return fmt.Sprintf("%.2fKB", float64(size)/float64(1024))
	case size < 1024*1024*1024:
		return fmt.Sprintf("%.2fMB", float64(size)/float64(1024*1024))
	case size < 1024*1024*1024*1024:
		return fmt.Sprintf("%.2fGB", float64(size)/float64(1024*1024*1024))
	case size < 1024*1024*1024*1024*1024:
		return fmt.Sprintf("%.2fTB", float64(size)/float64(1024*1024*1024*1024))
	default:
		return fmt.Sprintf("%.2fPB", float64(size)/float64(1024*1024*1024*1024*1024))
	}
}
